package org.offsync.async

object AsyncResponder {
  /** Creates a responder that answer continuations with the constant
   *  <code>a</code>.
   *
   *  @param x ...
   *  @return ...
   */
  def constant[A](x: () => A) = new AsyncResponder[A] {
    def respond(k: (() => A) => Unit) = k(x)
  }

  /** Executes <code>x</code> and returns <code>true</code>, useful
   *  as syntactic convenience in for comprehensions.
   *
   *  @param x ...
   *  @return ...
   */
  def exec[A](x: => Unit): Boolean = { x; true }

  /** runs a responder, returning an optional result
  */
  def run[A](r: AsyncResponder[A]): Option[() => A] = {
    var result: Option[() => A] = None
    r.foreach(x => result = Some(x))
    result
  }

  def loop[A](r: AsyncResponder[Unit]): AsyncResponder[Nothing] = 
    for (_ <- r; val y <- loop(r)) yield y

  def loopWhile[A](cond: => Boolean)(r: AsyncResponder[Unit]): AsyncResponder[Unit] = 
    if (cond) for (_ <- r; val y <- loopWhile(cond)(r)) yield y
    else constant({() => })
}

abstract class AsyncResponder[+A] extends Serializable {
  def apply(k: (() => A) => Unit): Unit = respond(k)
  
  def respond(k: (() => A) => Unit): Unit

  def foreach(k: (() => A) => Unit) { respond(k) }

  def map[B](f: A => B) = new AsyncResponder[B] {
    def respond(k: (() => B) => Unit) {
      AsyncResponder.this.respond(x => k { () => f(x()) } )
    }
  }

  def flatMap[B](f: A => AsyncResponder[B]) = new AsyncResponder[B] {
    def respond(k: (() => B) => Unit) {
      AsyncResponder.this.respond(x => f(x()).respond(k))
    }
  }

  def filter(p: A => Boolean) = new AsyncResponder[A] {
    def respond(k: (() => A) => Unit) {
      AsyncResponder.this.respond(x => if (p(x())) k(x) else ())
    }
  }

  override def toString = "Responder"

  def ~[U](that: AsyncResponder[U]) = {
    for (t <- this; u <- that) yield (t, u)
  }
}
